﻿using AutoMapper;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Threading.Tasks;

using KC.Framework.Base;
using KC.Framework.Extension;
using KC.Framework.Tenant;
using KC.Service.Core.Constants;
using KC.Service.Core.WebApiService.Business;
using KC.DTO.Account;
using KC.Service.Core.Base;
using System.Data;
using KC.Common;
using System.Reflection;
using System.ComponentModel;

namespace KC.Service.Core.EFService
{
    public interface IEFService
    {
        Tenant Tenant { get; set; }

        Task<List<UserDTO>> GetSubordinateUsersByOrgId(int orgId);
        Task<List<UserDTO>> GetSubordinateUsersByUserId(string userId);
    
    }

    public abstract class EFServiceBase : IEFService
    {
        protected readonly IHttpClientFactory HttpClientFactory;
        protected readonly IServiceProvider ServiceProvider;
        protected readonly ILogger Logger;

        protected readonly IAccountApiService AccountApiService;
        protected readonly ITenantUserApiService TenantUserApiService;
        protected readonly IConfigApiService ConfigApiService;

        protected EFServiceBase(
            IAccountApiService accountApiService,
            ITenantUserApiService tenantUserApiService,
            IConfigApiService configApiService,
            IHttpClientFactory clientFactory,
            ILogger logger)
        {
            AccountApiService = accountApiService;
            TenantUserApiService = tenantUserApiService;
            ConfigApiService = configApiService;

            HttpClientFactory = clientFactory;
            Logger = logger;
        }

        //protected EFServiceBase(
        //    Tenant tenant, 
        //    IHttpClientFactory clientFactory)
        //{
        //    Tenant = tenant;
        //    httpClientFactory = clientFactory;
        //}

        protected EFServiceBase(
            Tenant tenant,
            IAccountApiService accountApiService,
            ITenantUserApiService tenantUserApiService,
            IConfigApiService configApiService,
            IHttpClientFactory clientFactory,
            ILogger logger)
        {
            Tenant = tenant;

            AccountApiService = accountApiService;
            TenantUserApiService = tenantUserApiService;
            ConfigApiService = configApiService;

            HttpClientFactory = clientFactory;
            Logger = logger;
        }

        public Tenant Tenant { get; set; }

        /// <summary>
        /// 获取系统编码
        /// </summary>
        /// <param name="name">配置名称</param>
        /// <param name="step">步长</param>
        /// <returns></returns>
        public SeedEntity GenerateIdByName(SeedType name, int step = 1)
        {
            try
            {
                return ConfigApiService.GenerateIdByName(name.ToString(), step);
            }
            catch
            {
                return null;
            }
        }

        #region config

        public CallConfig CallConfig
        {
            get
            {
                var config = ConfigApiService.GetTenantCallUncallConfig(Tenant);
                return config;
            }
        }
        public EmailConfig EmailConfig
        {
            get
            {
                var config = ConfigApiService.GetTenantEmailConfig(Tenant);
                return config;
            }
        }
        public SmsConfig SmsNoticeConfig
        {
            get
            {
                var config = ConfigApiService.GetTenantSmsCLConfig(Tenant, SmsType.Notice);
                return config;
            }
        }
        public SmsConfig SmsMarketConfig
        {
            get
            {
                var config = ConfigApiService.GetTenantSmsCLConfig(Tenant, SmsType.Marketing);
                return config;
            }
        }
        public SmsConfig SmsVoiceConfig
        {
            get
            {
                var config = ConfigApiService.GetTenantSmsCLConfig(Tenant, SmsType.VoiceCaptcha);
                return config;
            }
        }

        //public WeixinConfig WeixinConfig
        //{
        //    get
        //    {
        //        var config = WeixinUtil.GetTenantWeixinConfig(Tenant);
        //        return config;
        //    }
        //}

        #endregion

        /// <summary>
        /// 获取该组织下的所有员工，包括：该组织下的所有子组织
        /// </summary>
        /// <param name="orgId"></param>
        /// <returns></returns>
        public async Task<List<UserDTO>> GetSubordinateUsersByOrgId(int orgId)
        {
            var cacheKey = CacheKeyConstants.Prefix.TenantName + Tenant.TenantName + "-GetSubordinateUsersByOrgId-" + orgId;
            var cachedData = Service.Core.CacheUtil.GetCache<List<UserDTO>>(cacheKey);
            if (cachedData != null)
                return cachedData;

            var user = await AccountApiService.GetUsersByOrgId(orgId);
            if (user == null)
                return new List<UserDTO>(); ;

            if (user.Any())
            {
                Service.Core.CacheUtil.SetCache(cacheKey, user, TimeSpan.FromMinutes(TimeOutConstants.CacheTimeOut));
            }

            return user;
        }

        /// <summary>
        /// 获取与该员工相关的下属组织的所有员工：
        ///     如果，该员工为：管理岗，则返回所属的下级组织的所有员工；
        ///     如果，该员工为：职员，则返回该员工的数据；
        /// </summary>
        /// <param name="userId"></param>
        /// <returns></returns>
        public async Task<List<UserDTO>> GetSubordinateUsersByUserId(string userId)
        {
            var cacheKey = CacheKeyConstants.Prefix.TenantName + Tenant.TenantName + "-GetSubordinateUsersByUserId-" + userId;
            var cachedData = Service.Core.CacheUtil.GetCache<List<UserDTO>>(cacheKey);
            if (cachedData != null)
                return cachedData;

            var result = new List<UserDTO>();
            var user = await AccountApiService.GetUserWithOrganizationsByUserId(userId);
            if (user == null)
                return result;

            switch (user.PositionLevel)
            {
                //如果员工为职员，则返回该员工的数据；
                case PositionLevel.Staff:
                    result.Add(Mapper.Map<UserDTO>(user));
                    break;
                //如果员工为管理岗，则返回所属的下级组织的所有员工；
                case PositionLevel.DepartmentMananger:
                case PositionLevel.DepartmentSubMananger:
                    var orgIds = user.Organizations.Select(m => m.id).ToList();
                    var users = AccountApiService.GetUsersByOrgIds(orgIds).Result;
                    if (users != null && users.Any())
                    {
                        result.AddRange(Mapper.Map<List<UserDTO>>(users));
                    }
                    break;
            }

            if (result.Any())
            {
                Service.Core.CacheUtil.SetCache(cacheKey, result, TimeSpan.FromMinutes(TimeOutConstants.CacheTimeOut));
            }

            return result;
        }

        /// <summary>
        /// 获取某操作人的部门，兄弟部门，下级部门，父级部门Ids
        /// </summary>
        /// <param name="currentUserId">当前用户Id</param>
        /// <param name="includeCurrentOrganizations">是否包括本部门</param>
        /// <param name="includeSiblingsOrganizationIds">是否包括兄弟部门</param>
        /// <param name="includeChildOrganizaitons">是否包括子部门</param>
        /// <param name="includeParentOrganizaitons">是否包括父级部门</param>
        /// <returns></returns>
        public async Task<List<int>> GetCurrentUserRelationOrganizationIds(string currentUserId,
            bool includeCurrentOrganizations = true, bool includeSiblingsOrganizationIds = true,
            bool includeChildOrganizaitons = false, bool includeParentOrganizaitons = false)
        {
            List<int> organizationIds = new List<int>();
            //当前操作人部门
            var currentUserOrganizations = await AccountApiService.GetOrganizationsWithUsersByUserId(currentUserId);
            if (currentUserOrganizations == null || !currentUserOrganizations.Any())
                return organizationIds;
            
            //所有部门
            var organizations = await AccountApiService.GetAllOrganization();
            if (organizations == null || !organizations.Any())
                return organizationIds;

            //当前操作人部门Ids
            if (includeCurrentOrganizations)
            {
                var currentUserOrganizationIds = currentUserOrganizations.Select(o => o.id);
                organizationIds.AddRange(currentUserOrganizationIds);
            }

            //当前操作人同级（兄弟）部门Ids
            if (includeSiblingsOrganizationIds)
            {
                //当前操作人父级部门
                var currentUserParentOrganizations =
                    organizations.Where(o => currentUserOrganizations.Any(p => p.ParentId == o.id));
                var currentUserSiblingsOrganizationIds =
                    organizations.Where(o => currentUserParentOrganizations.Any(p => p.id == o.ParentId))
                        .Select(o => o.id);
                organizationIds.AddRange(currentUserSiblingsOrganizationIds);
            }

            //当前操作人子部门Ids
            if (includeChildOrganizaitons)
            {
                var currentUserChildOrganizationIds =
                    organizations.Where(o => currentUserOrganizations.Any(p => p.id == o.ParentId))
                        .Select(q => q.id);
                organizationIds.AddRange(currentUserChildOrganizationIds);
            }

            //当前操作人父级部门Ids
            if (includeParentOrganizaitons)
            {
                var currentUserParentOrganizationIds =
                    organizations.Where(o => currentUserOrganizations.Any(p => p.ParentId == o.id))
                        .Select(q => q.id);
                organizationIds.AddRange(currentUserParentOrganizationIds);
            }
            return organizationIds;
        }

        /// <summary>
        /// 扩展方法，将一个Json字符串反序列化为DataTable
        /// </summary>
        /// <typeparam name="T">类型</typeparam>
        /// <param name="str"></param>
        /// <returns>DataTable</returns>
        public DataTable DerializeToDataTable<T>(string str)
        {
            DataTable dt = new DataTable();
            if (str[0] == '[')//如果str的第一个字符是'['，则说明str里存放有多个model数据
            {
                //删除最后一个']'和第一个'['，顺序不能错。不然字符串的长度就不对了。
                //因为每个model与model之间是用 ","分隔的，所以改为用 ";"分隔
                str = str.Remove(str.Length - 1, 1).Remove(0, 1).Replace("},{", "};{");
            }

            string[] items = str.Split(';');//用";"分隔开多条数据
            foreach (PropertyInfo property in typeof(T).GetProperties())//反射，获得T类型的所有属性
            {
                var columnType = property.PropertyType;
                //判断是否可为空类型
                if (columnType.IsGenericType && columnType.GetGenericTypeDefinition().Equals(typeof(Nullable<>)))
                {
                    columnType = property.PropertyType.GetGenericArguments()[0];
                }

                var attribute = property.GetCustomAttribute<DisplayNameAttribute>();

                string displayName = String.Empty;
                if (attribute != null)
                {
                    displayName = attribute.DisplayName;
                }


                //创建一个新列，列名为属性名，类型为属性的类型。
                if (!String.IsNullOrEmpty(displayName))
                {
                    DataColumn col = new DataColumn(displayName, columnType);//property.Name, property.PropertyType);
                    dt.Columns.Add(col);
                }

            }

            //循环，一个一个的反序列化
            for (int i = 0; i < items.Length; i++)
            {
                //创建新行
                DataRow dr = dt.NewRow();

                //反序列化为一个T类型对象
                T temp = SerializeHelper.FromJson<T>(items[i]);
                foreach (PropertyInfo property in typeof(T).GetProperties())
                {
                    //赋值
                    //dr[property.Name] = property.GetValue(temp, null);
                    var attribute = property.GetCustomAttribute<DisplayNameAttribute>();

                    string displayName = String.Empty;
                    if (attribute != null)
                    {
                        displayName = attribute.DisplayName;
                        if (dr[displayName] != null)
                        {
                            dr[displayName] = property.GetValue(temp, null);
                        }
                    }


                }
                dt.Rows.Add(dr);
            }
            return dt;
        }


        public static void RegisterEntityFrameworkExtensions(string licenseName, string licenseKey)
        {
            Z.EntityFramework.Extensions.LicenseManager.AddLicense(licenseName, licenseKey);
        }

        public static bool ValidZZZEntityFrameworkExtensions()
        {
            return Z.EntityFramework.Extensions.LicenseManager.ValidateLicense();
        }
    }

    //[LoggingCallHandler]
    public interface IWebApiService
    {
        Tenant Tenant { get; set; }
    }

    public abstract class WebApiServiceBase : IWebApiService
    {
        protected readonly IHttpClientFactory httpClientFactory;
        protected readonly ILogger Logger;

        protected WebApiServiceBase(
            Tenant tenant)
        {
            Tenant = tenant ?? TenantConstant.DbaTenantApiAccessInfo;
        }

        protected WebApiServiceBase(
            Tenant tenant,
            IHttpClientFactory clientFactory,
            ILogger logger)
        {
            Tenant = tenant;
            httpClientFactory = clientFactory;
            Logger = logger;
        }

        public Tenant Tenant { get; set; }
    }
}
